package ru.gerunn.silent.core;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.apache.log4j.Logger;

import ru.gerunn.silent.io.ConsoleReader;
import ru.gerunn.silent.io.ConsoleWriter;
import ru.gerunn.silent.io.Reader;
import ru.gerunn.silent.io.ReaderException;
import ru.gerunn.silent.io.SocketReader;
import ru.gerunn.silent.io.SocketWriter;
import ru.gerunn.silent.io.Writer;
import ru.gerunn.silent.io.WriterException;
import ru.gerunn.silent.message.Message;

public class Client {
	public static Logger log = Logger.getLogger(Client.class);

	private final int port;
	private final SSLSocket socket;

	public Client() throws ClientException {
		this(8883);
	}

	public Client(int port) throws ClientException {
		log.info("client creation start");
		this.port = port;
		SSLSocketFactory socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
		SSLSocket socket = null;
		try {
			socket = (SSLSocket) socketFactory.createSocket("localhost", this.port);
		} catch (UnknownHostException e) {
			throw new ClientException(e);
		} catch (IOException e) {
			throw new ClientException(e);
		}

		try {
			socket.startHandshake();
		} catch (IOException e) {
			throw new ClientException(e);
		}
		this.socket = socket;
		log.info("client creation finish");
	}

	public void start() throws ClientException {
		try {
			final Writer consoleWriter = new ConsoleWriter();
			final Writer socketWriter = new SocketWriter(socket);
			final Reader consoleReader = new ConsoleReader();
			final Reader socketReader = new SocketReader(socket);

			ExecutorService pool = Executors.newFixedThreadPool(2);

			Future<Object> serverClientFuture = pool.submit(new Callable<Object>() {
				public Object call() throws ClientException {
					Message message = null;
					try {
						while ((message = consoleReader.read()) != null) {
							socketWriter.write(message);
						}
					} catch (ReaderException e) {
						throw new ClientException(e);
					} catch (WriterException e) {
						throw new ClientException(e);
					}
					return null;
				}
			});

			Future<Object> clientServerFuture = pool.submit(new Callable<Object>() {
				public Object call() throws ClientException {
					Message message = null;
					try {
						while ((message = socketReader.read()) != null) {
							consoleWriter.write(message);
						}
					} catch (ReaderException e) {
						throw new ClientException(e);
					} catch (WriterException e) {
						throw new ClientException(e);
					}
					return null;
				}
			});

			pool.shutdown();

			serverClientFuture.get();
			clientServerFuture.get();

		} catch (WriterException e) {
			throw new ClientException(e);
		} catch (ReaderException e) {
			throw new ClientException(e);
		} catch (InterruptedException e) {
			throw new ClientException(e);
		} catch (ExecutionException e) {
			throw new ClientException(e);
		}
	}
}
